
#pragma once

#include "gl/glew.h"
#include "ublocks.h"

#include <vector>
#include <string>
#include <map>

using namespace std;

// Single pass technique
class IProgram {
protected:
    GLuint              m_programId;
    
    struct TexUnit {
        GLint   m_location;
        GLuint  m_unit;
        GLuint  m_sampler;
        GLenum  m_target;
    };
    map<string, TexUnit>  m_texUnits;
    map<string, GLint>  m_attribMap;
    vector<const UniformBlock*> m_uBlocks;

    mutable GLint       m_targetTex;
    mutable GLenum      m_targetType;

    const string    m_name;
    const string    m_alias;
    GLuint          m_fbo;

    void CompileShader(GLuint shader, const string& src, bool verbose=true);
    void LinkProgram(GLuint programId, bool verbose=true);
    
    void SetSourceTextures() const;
    virtual void ResetStateToProgram() const=0;
    virtual void GetActiveUniforms(const UniformMap& uBlocks)=0;

public:
    IProgram(const string& name, const string& alias);
    virtual ~IProgram();
    
    GLint GetAttribLocation(const string& attr) const;

    void SetTargetTexture(GLuint texId, GLenum target) const;
    void SetTexture(const string& texName, GLuint texId) const;
    
    virtual void DrawPass(int pass=0) const = 0;

    void AttachGlobalUniforms(const UniformMap& uBlocks);

    void ResetToProgram() const;
};
typedef map<string, IProgram*> ProgramsMap;



class PresentTechnique : public IProgram {
protected:
    GLint   m_viewportX, m_viewportY;
    GLsizei m_viewportWidth, m_viewportHeight;

    PresentTechnique(const string& name, const string& alias);

    void ResetStateToProgram() const;
    void GetActiveUniforms(const UniformMap& uBlocks);

public:

    PresentTechnique(const string& shaderName);
    ~PresentTechnique();

    void DrawPass(int pass=0) const;
    void SetImgTexture(GLuint tex) const;

    void SetViewport(GLint x, GLint y, GLsizei width, GLsizei height);
};

// Gaussian with radius of 1, variable weights
class WaveSolveTechnique : public IProgram {
protected:
    GLuint&  m_GaussianProgramId;
    GLuint  m_waveProgramId;
    GLuint  m_tmpTex;
    GLsizei m_maxWidth, m_maxHeight;
    mutable GLsizei m_width, m_height;

    vector<GLint>   m_WaveTexUnits;

    GLint   m_uniformDir;

    GLuint  m_vao;
    GLuint  m_vbo;

public:
    WaveSolveTechnique(GLsizei maxWidth, GLsizei maxHeight);
    ~WaveSolveTechnique();

    void DrawPass(int pass=0) const;
    void AttachGlobalUniforms(const UniformMap& uBlocks);
    void SetTextureSize(GLsizei width, GLsizei height) const;
};

class DisplacedQuadTechnique : public PresentTechnique {
private:
    GLuint  m_material;

protected:
    void GetActiveUniforms(const UniformMap& uBlocks);

public:
    DisplacedQuadTechnique(const string& shaderName);
    ~DisplacedQuadTechnique();

    void SetDispTexture(GLuint tex) const;
};
